package com.lukstron.youko;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MeasureActivity extends YoukoActivity {

	/**
	 * Thread that do the measurement.
	 */
	protected StartThread _startThread = null;

	/**
	 * Set view to idle state.
	 */
	protected Runnable _idling = new Runnable() {
		public void run() {
			idle();
		}
	};

	/**
	 * Increment progress bar.
	 */
	protected Runnable _incrementingProgress = new Runnable() {
		public void run() {
			incrementProgress();
		}
	};

	/**
	 * Set view to paused state.
	 */
	protected Runnable _pausing = new Runnable() {
		public void run() {
			setView("paused");
		}
	};

	/**
	 * Handler to update GUI.
	 */
	protected final Handler _handler = new Handler() {

		@Override
		public void handleMessage(Message message) {
			Bundle bundle = message.getData();
			String string = null;
			string = bundle.getString("probe");
			if (string != null) {
				setProbe(string);
			}
			string = bundle.getString("battery");
			if (string != null) {
				setBattery(string);
			}
			string = bundle.getString("datum");
			if (string != null) {
				addData(string);
			}
			string = bundle.getString("progress");
			if (string != null) {
				setProgress(string);
			}
			string = bundle.getString("toast");
			if (string != null) {
				toast(string);
			}
		}

	};

	/**
	 * Content of data list view.
	 */
	protected ArrayList<String> _dataList = new ArrayList<String>();

	/**
	 * Adapter of data list view.
	 */
	protected ArrayAdapter<String> _dataAdapter = null;

	protected class StartThread extends Thread {

		protected Boolean _stopped = null;
		protected Boolean _paused = null;
		protected String _path = null;
		protected Hardware _hardware = null;
		protected List<Datum> _data = null;

		public StartThread(Hardware hardware, List<Datum> data, String path) {
			this._hardware = hardware;
			this._data = data;
			this._path = path;
		}

		@Override
		public void interrupt() {
			super.interrupt();
			_paused = false;
			_stopped = true;
		}

		@Override
		public void run() {
			if (!_hardware.connect()) {
				_sendString("toast", "Could not connect to the hardware.");
			} else {
				_measure();
			}
			MeasureActivity.this._handler.post(_idling);
		}

		protected void _measure() {
			DataWriter log;
			try {
				log = new DataWriter(_path + ".txt");
			} catch (IOException e) {
				return;
			}

			SharedPreferences pref = null;
			pref = getSharedPreferences("GeoRes", Context.MODE_PRIVATE);
			Integer mode = pref.getInt("mode", 0);

			_stopped = false;
			_paused = false;

			try {
				for (Datum each : _data) {
					each.date = new Date();

					// Update probe information in view to reflect current datum
					_sendString("probe", each.toProbeString());

					_sendString("progress", "Checking battery...");
					Battery battery = _measureBattery(each);
					if (battery != null) {
						_sendString("battery", battery.toString());
					} else {
						_sendString("battery", "Failed");
					}

					// Measure and update datum information in view
					_sendString("progress", "Measuring...");
					Datum datum = null;
					if (mode == 0) {
						datum = _measureResistivity(each);
					} else {
						datum = _measureInducePolarization(each);
					}
					if (datum != null) {
						_sendString("datum", datum.toDatumString());
						// Write datum to log
						log.append(datum.toString());
						log.append("\n");
					} else {
						_sendString("datum", "Failed");
					}

					MeasureActivity.this._handler.post(_incrementingProgress);

					if (_stopped) {
						break;
					}

					if (_paused) {
						MeasureActivity.this._handler.post(_pausing);
						while (_paused) {
							sleep(100);
						}
					}
				}
			} catch (Exception e) {

			}
			log.close();
		}

		/**
		 * Measure battery.
		 * 
		 * @param datum
		 */
		protected Battery _measureBattery(Datum datum) {
			if (_hardware instanceof HardwareSimulator) {
				try {
					sleep(500);
				} catch (InterruptedException e) {

				}
			}

			Battery battery = new Battery();
			battery.hasCurrent = _hardware.getCurrent();
			battery.hasPotential = _hardware.getPotential();
			battery.primary = _hardware.measureBattery(1);
			battery.secondary = _hardware.measureBattery(2);
			return battery;
		}

		public void pause() {
			_paused = true;
		}

		public void recover() {
			_paused = false;
		}

		/**
		 * Measure resistivity.
		 * 
		 * @param datum
		 * @return
		 */
		protected Datum _measureResistivity(Datum datum) {
			if (_hardware instanceof HardwareSimulator) {
				try {
					sleep(500);
				} catch (InterruptedException e) {

				}
			}

			Random random = new Random();
			datum.selfPotential = (float) random.nextInt(100);
			datum.current = (float) random.nextInt(100);
			datum.potential = (float) random.nextInt(100);
			return datum;
		}

		/**
		 * Measure induce polarization.
		 * 
		 * @param datum
		 * @return
		 */
		protected Datum _measureInducePolarization(Datum datum) {
			if (_hardware instanceof HardwareSimulator) {
				try {
					sleep(500);
				} catch (InterruptedException e) {

				}
			}

			Random random = new Random();
			datum.selfPotential = (float) random.nextInt(100);
			datum.current = (float) random.nextInt(100);
			datum.potential = (float) random.nextInt(100);
			return datum;
		}

		/**
		 * Send string to handler for show in view.
		 * 
		 * @param key
		 * @param value
		 */
		protected void _sendString(String key, String value) {
			Message message = null;
			Bundle bundle = null;
			bundle = new Bundle();
			bundle.putString(key, value);
			message = new Message();
			message.setData(bundle);
			MeasureActivity.this._handler.sendMessage(message);
		}

	}

	/**
	 * Get filename.
	 * 
	 * @return filename without extension.
	 */
	public String getPath() {
		EditText editText = (EditText) findViewById(R.id.path);
		if (editText.getText().length() == 0) {
			String s = "Save as can not be blank.";
			Toast.makeText(this, s, Toast.LENGTH_LONG).show();
			return "";
		}
		return editText.getText().toString();
	}

	/**
	 * Get method.
	 * 
	 * @return selected method.
	 */
	public Method getMethod() {
		SharedPreferences pref = null;
		pref = getSharedPreferences("GeoRes", Context.MODE_PRIVATE);
		Method method = null;
		if (pref.getInt("method", 0) == 0) {
			method = new Shlumberger(pref.getInt("electrode", 16));
		} else {
			method = new Wenner(pref.getInt("electrode", 16));
		}
		return method;
	}

	/**
	 * Get sequence number to measure.
	 * 
	 * @return data sequence
	 */
	public Integer getSequence() {
		Integer sequence = 0;
		EditText editText = (EditText) findViewById(R.id.sequence);
		if (editText.getText().length() > 0) {
			try {
				sequence = Integer.parseInt(editText.getText().toString());
			} catch (Exception e) {
				sequence = null;

				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.append(editText.getText().toString());
				String s = stringBuilder.toString();
				Toast.makeText(this, s, Toast.LENGTH_LONG).show();
			}
		}
		return sequence;
	}

	/**
	 * Set probe text.
	 * 
	 * @param datum
	 */
	public void setProbe(String string) {
		TextView textView = (TextView) findViewById(R.id.probe);
		textView.setText(string);
	}

	/**
	 * Set battery text.
	 * 
	 * @param battery
	 */
	public void setBattery(String string) {
		TextView textView = (TextView) findViewById(R.id.battery);
		textView.setText(string);
	}

	/**
	 * Set progress text.
	 * 
	 * @param string
	 */
	public void setProgress(String string) {
		TextView textView = (TextView) findViewById(R.id.progressText);
		textView.setText(string);
	}

	/**
	 * Increment progress bar by 1.
	 */
	public void incrementProgress() {
		ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
		progressBar.incrementProgressBy(1);
	}

	/**
	 * Add data.
	 * 
	 * @param string
	 */
	public void addData(String string) {
		_dataList.add(string);
		_dataAdapter.notifyDataSetChanged();
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.measure);
		_initData();
		_bindStart();
		_bindStop();
		_bindPause();
		_bindResume();
		setView("idle");
	}

	/**
	 * Show toast in GUI thread.
	 * 
	 * @param string
	 */
	public void toast(String string) {
		Toast.makeText(this, string, Toast.LENGTH_LONG).show();
	}

	/**
	 * Initialize data list view.
	 */
	public void _initData() {
		int i = R.layout.simple_list_item_1;
		_dataAdapter = new ArrayAdapter<String>(this, i, _dataList);
		ListView view = (ListView) findViewById(R.id.data);
		view.setAdapter(_dataAdapter);
	}

	/**
	 * Bind start button.
	 */
	protected void _bindStart() {
		Button button = (Button) findViewById(R.id.start);
		OnClickListener listener = null;
		listener = new OnClickListener() {
			public void onClick(View v) {
				start();
			}
		};
		button.setOnClickListener(listener);
	}

	/**
	 * Start measurement.
	 */
	public void start() {
		if (!(hasSd(this) && getPath().length() > 0)) {
			return;
		}

		Integer sequence = getSequence();
		if (sequence == null) {
			return;
		}

		acquireWakeLock();

		List<Datum> data = null;

		if (sequence > 0) {

		} else {
			SharedPreferences pref = null;
			pref = getSharedPreferences("GeoRes", Context.MODE_PRIVATE);

			IMethod method = (IMethod) getMethod();

			ProgressBar progressBar = null;
			progressBar = (ProgressBar) findViewById(R.id.progressBar);
			int limit = pref.getInt("limit", 0);
			progressBar.setMax(method.count(limit));
			progressBar.setProgress(0);

			data = method.getData(limit);
		}

		setView("start");
		Hardware hardware = new HardwareSimulator();
		_startThread = new StartThread(hardware, data, getPath());
		_startThread.start();
	}

	/**
	 * Bind stop button.
	 */
	protected void _bindStop() {
		Button button = (Button) findViewById(R.id.stop);
		OnClickListener listener = null;
		listener = new OnClickListener() {
			public void onClick(View v) {
				stop();
			}
		};
		button.setOnClickListener(listener);
	}

	/**
	 * Stop measurement.
	 */
	public void stop() {
		setView("stop");
		_startThread.interrupt();
	}

	/**
	 * Set view to idle state and restore auto sleep.
	 * 
	 * This function must be called when thread finished whether operation
	 * complete or aborted.
	 */
	public void idle() {
		releaseWakeLock();
		setView("idle");
	}

	/**
	 * Bind pause button.
	 */
	protected void _bindPause() {
		Button button = (Button) findViewById(R.id.pause);
		OnClickListener listener = null;
		listener = new OnClickListener() {
			public void onClick(View v) {
				pause();
			}
		};
		button.setOnClickListener(listener);
	}

	/**
	 * Pause measurement.
	 */
	protected void pause() {
		setView("pause");
		_startThread.pause();
	}

	/**
	 * Bind resume button.
	 */
	protected void _bindResume() {
		Button button = (Button) findViewById(R.id.resume);
		OnClickListener listener = null;
		listener = new OnClickListener() {
			public void onClick(View v) {
				resume();
			}
		};
		button.setOnClickListener(listener);
	}

	/**
	 * Resume measurement.
	 */
	public void resume() {
		setView("resume");
		_startThread.recover();
	}

	@Override
	protected void onPause() {
		super.onPause();
		if (_startThread != null && _startThread.isAlive()) {
			_startThread.interrupt();
		}
	}

	/**
	 * Set view.
	 * 
	 * @param mode
	 *            [idle|start|stop|pause|resume]
	 */
	public void setView(String mode) {
		Button start = (Button) findViewById(R.id.start);
		Button stop = (Button) findViewById(R.id.stop);
		Button pause = (Button) findViewById(R.id.pause);
		Button resume = (Button) findViewById(R.id.resume);
		ProgressBar progressBarInfinite = null;
		int id = R.id.progressBarInfinite;
		progressBarInfinite = (ProgressBar) findViewById(id);
		ProgressBar progressBar = null;
		progressBar = (ProgressBar) findViewById(R.id.progressBar);
		TextView progressText = (TextView) findViewById(R.id.progressText);
		if (mode == "idle") {
			progressBar.setVisibility(View.GONE);
			progressBarInfinite.setVisibility(View.GONE);
			progressText.setVisibility(View.GONE);
			start.setEnabled(true);
			stop.setEnabled(false);
			pause.setEnabled(false);
			resume.setEnabled(false);
		} else if (mode == "start") {
			progressBar.setVisibility(View.VISIBLE);
			progressBarInfinite.setVisibility(View.VISIBLE);
			progressText.setText("Initializing, please wait...");
			progressText.setVisibility(View.VISIBLE);
			start.setEnabled(false);
			stop.setEnabled(true);
			pause.setEnabled(true);
			resume.setEnabled(false);
		} else if (mode == "stop") {
			stop.setEnabled(false);
			pause.setEnabled(false);
		} else if (mode == "pause") {
			stop.setEnabled(false);
			pause.setEnabled(false);
		} else if (mode == "paused") {
			progressText.setText("Paused");
			stop.setEnabled(true);
			resume.setEnabled(true);
		} else if (mode == "resume") {
			pause.setEnabled(true);
			resume.setEnabled(false);
		} else {

		}
	}

}
